/*
 ============================================================================
 Name        : Project of Rosilda
 Author      : Emeson J. S. Pereira
 Version     :
 Copyright   : Your copyright notice
 Description : Project of Classification in Complex Numbers in C, Ansi-style
 ============================================================================
 */
//=========================================

#define ITERATIONS 100000
#define CLASSES 3
#define DIM 4
#define POINTS 150
#define NUM_PARENTS 6
#define NUM_OFFSPRINGS_PER_PARENTS 4

//============Modulos==========================

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "MersenneTwister.h"
#include "Functions.h"

//============Struct===========================

struct individuo{
	float A[POINTS*DIM];
	float B[POINTS*DIM];
	float sigma[POINTS*DIM];
	float signal[POINTS*DIM];
	float fitness;
} parents[NUM_PARENTS], offsprings[NUM_OFFSPRINGS_PER_PARENTS*NUM_PARENTS];

//============Functions New Algorithm==========

void createIndividuo( struct individuo *individuo);
void fitness( struct individuo *individuo);

//============Functions ES=====================

void createParents(struct individuo *parents_);
void mutation(struct individuo *parents_, struct individuo *offsprings_);
long worstIndividuo( struct individuo *list_individuo, long dimension );
long bestIndividuo( struct individuo *list_individuo, long dimension );
void survivorSelection(struct individuo *parents_, struct individuo *offsprings_);

//============Variables========================

float tau, tau_;
const int points_classes[CLASSES] = {50,50,50};

const float matrix_z[POINTS*DIM] = {0.222222,	0.625000,	0.067797,	0.041667,
0.166667,	0.416667,	0.067797,	0.041667,
0.111111,	0.500000,	0.050847,	0.041667,
0.083333,	0.458333,	0.084746,	0.041667,
0.194444,	0.666667,	0.067797,	0.041667,
0.305556,	0.791667,	0.118644,	0.125000,
0.083333,	0.583333,	0.067797,	0.083333,
0.194444,	0.583333,	0.084746,	0.041667,
0.027778,	0.375000,	0.067797,	0.041667,
0.166667,	0.458333,	0.084746,	0.000000,
0.305556,	0.708333,	0.084746,	0.041667,
0.138889,	0.583333,	0.101695,	0.041667,
0.138889,	0.416667,	0.067797,	0.000000,
0.000000,	0.416667,	0.016949,	0.000000,
0.416667,	0.833333,	0.033898,	0.041667,
0.388889,	1.000000,	0.084746,	0.125000,
0.305556,	0.791667,	0.050847,	0.125000,
0.222222,	0.625000,	0.067797,	0.083333,
0.388889,	0.750000,	0.118644,	0.083333,
0.222222,	0.750000,	0.084746,	0.083333,
0.305556,	0.583333,	0.118644,	0.041667,
0.222222,	0.708333,	0.084746,	0.125000,
0.083333,	0.666667,	0.000000,	0.041667,
0.222222,	0.541667,	0.118644,	0.166667,
0.138889,	0.583333,	0.152542,	0.041667,
0.194444,	0.416667,	0.101695,	0.041667,
0.194444,	0.583333,	0.101695,	0.125000,
0.250000,	0.625000,	0.084746,	0.041667,
0.250000,	0.583333,	0.067797,	0.041667,
0.111111,	0.500000,	0.101695,	0.041667,
0.138889,	0.458333,	0.101695,	0.041667,
0.305556,	0.583333,	0.084746,	0.125000,
0.250000,	0.875000,	0.084746,	0.000000,
0.333333,	0.916667,	0.067797,	0.041667,
0.166667,	0.458333,	0.084746,	0.000000,
0.194444,	0.500000,	0.033898,	0.041667,
0.333333,	0.625000,	0.050847,	0.041667,
0.166667,	0.458333,	0.084746,	0.000000,
0.027778,	0.416667,	0.050847,	0.041667,
0.222222,	0.583333,	0.084746,	0.041667,
0.194444,	0.625000,	0.050847,	0.083333,
0.055556,	0.125000,	0.050847,	0.083333,
0.027778,	0.500000,	0.050847,	0.041667,
0.194444,	0.625000,	0.101695,	0.208333,
0.222222,	0.750000,	0.152542,	0.125000,
0.138889,	0.416667,	0.067797,	0.083333,
0.222222,	0.750000,	0.101695,	0.041667,
0.083333,	0.500000,	0.067797,	0.041667,
0.277778,	0.708333,	0.084746,	0.041667,
0.194444,	0.541667,	0.067797,	0.041667,
0.750000,	0.500000,	0.627119,	0.541667,
0.583333,	0.500000,	0.593220,	0.583333,
0.722222,	0.458333,	0.661017,	0.583333,
0.333333,	0.125000,	0.508475,	0.500000,
0.611111,	0.333333,	0.610169,	0.583333,
0.388889,	0.333333,	0.593220,	0.500000,
0.555556,	0.541667,	0.627119,	0.625000,
0.166667,	0.166667,	0.389831,	0.375000,
0.638889,	0.375000,	0.610169,	0.500000,
0.250000,	0.291667,	0.491525,	0.541667,
0.194444,	0.000000,	0.423729,	0.375000,
0.444444,	0.416667,	0.542373,	0.583333,
0.472222,	0.083333,	0.508475,	0.375000,
0.500000,	0.375000,	0.627119,	0.541667,
0.361111,	0.375000,	0.440678,	0.500000,
0.666667,	0.458333,	0.576271,	0.541667,
0.361111,	0.416667,	0.593220,	0.583333,
0.416667,	0.291667,	0.525424,	0.375000,
0.527778,	0.083333,	0.593220,	0.583333,
0.361111,	0.208333,	0.491525,	0.416667,
0.444444,	0.500000,	0.644068,	0.708333,
0.500000,	0.333333,	0.508475,	0.500000,
0.555556,	0.208333,	0.661017,	0.583333,
0.500000,	0.333333,	0.627119,	0.458333,
0.583333,	0.375000,	0.559322,	0.500000,
0.638889,	0.416667,	0.576271,	0.541667,
0.694444,	0.333333,	0.644068,	0.541667,
0.666667,	0.416667,	0.677966,	0.666667,
0.472222,	0.375000,	0.593220,	0.583333,
0.388889,	0.250000,	0.423729,	0.375000,
0.333333,	0.166667,	0.474576,	0.416667,
0.333333,	0.166667,	0.457627,	0.375000,
0.416667,	0.291667,	0.491525,	0.458333,
0.472222,	0.291667,	0.694915,	0.625000,
0.305556,	0.416667,	0.593220,	0.583333,
0.472222,	0.583333,	0.593220,	0.625000,
0.666667,	0.458333,	0.627119,	0.583333,
0.555556,	0.125000,	0.576271,	0.500000,
0.361111,	0.416667,	0.525424,	0.500000,
0.333333,	0.208333,	0.508475,	0.500000,
0.333333,	0.250000,	0.576271,	0.458333,
0.500000,	0.416667,	0.610169,	0.541667,
0.416667,	0.250000,	0.508475,	0.458333,
0.194444,	0.125000,	0.389831,	0.375000,
0.361111,	0.291667,	0.542373,	0.500000,
0.388889,	0.416667,	0.542373,	0.458333,
0.388889,	0.375000,	0.542373,	0.500000,
0.527778,	0.375000,	0.559322,	0.500000,
0.222222,	0.208333,	0.338983,	0.416667,
0.388889,	0.333333,	0.525424,	0.500000,
0.555556,	0.541667,	0.847458,	1.000000,
0.416667,	0.291667,	0.694915,	0.750000,
0.777778,	0.416667,	0.830508,	0.833333,
0.555556,	0.375000,	0.779661,	0.708333,
0.611111,	0.416667,	0.813559,	0.875000,
0.916667,	0.416667,	0.949153,	0.833333,
0.166667,	0.208333,	0.593220,	0.666667,
0.833333,	0.375000,	0.898305,	0.708333,
0.666667,	0.208333,	0.813559,	0.708333,
0.805556,	0.666667,	0.864407,	1.000000,
0.611111,	0.500000,	0.694915,	0.791667,
0.583333,	0.291667,	0.728814,	0.750000,
0.694444,	0.416667,	0.762712,	0.833333,
0.388889,	0.208333,	0.677966,	0.791667,
0.416667,	0.333333,	0.694915,	0.958333,
0.583333,	0.500000,	0.728814,	0.916667,
0.611111,	0.416667,	0.762712,	0.708333,
0.944444,	0.750000,	0.966102,	0.875000,
0.944444,	0.250000,	1.000000,	0.916667,
0.472222,	0.083333,	0.677966,	0.583333,
0.722222,	0.500000,	0.796610,	0.916667,
0.361111,	0.333333,	0.661017,	0.791667,
0.944444,	0.333333,	0.966102,	0.791667,
0.555556,	0.291667,	0.661017,	0.708333,
0.666667,	0.541667,	0.796610,	0.833333,
0.805556,	0.500000,	0.847458,	0.708333,
0.527778,	0.333333,	0.644068,	0.708333,
0.500000,	0.416667,	0.661017,	0.708333,
0.583333,	0.333333,	0.779661,	0.833333,
0.805556,	0.416667,	0.813559,	0.625000,
0.861111,	0.333333,	0.864407,	0.750000,
1.000000,	0.750000,	0.915254,	0.791667,
0.583333,	0.333333,	0.779661,	0.875000,
0.555556,	0.333333,	0.694915,	0.583333,
0.500000,	0.250000,	0.779661,	0.541667,
0.944444,	0.416667,	0.864407,	0.916667,
0.555556,	0.583333,	0.779661,	0.958333,
0.583333,	0.458333,	0.762712,	0.708333,
0.472222,	0.416667,	0.644068,	0.708333,
0.722222,	0.458333,	0.745763,	0.833333,
0.666667,	0.458333,	0.779661,	0.958333,
0.722222,	0.458333,	0.694915,	0.916667,
0.416667,	0.291667,	0.694915,	0.750000,
0.694444,	0.500000,	0.830508,	0.916667,
0.666667,	0.541667,	0.796610,	1.000000,
0.666667,	0.416667,	0.711864,	0.916667,
0.555556,	0.208333,	0.677966,	0.750000,
0.611111,	0.416667,	0.711864,	0.791667,
0.527778,	0.583333,	0.745763,	0.916667,
0.444444,	0.416667,	0.694915,	0.708333};

FILE *file;

//=============================================

int main(void)
{

	unsigned long i, y, position;
	char str[80];
	float count_ = 0.1;

	tau  = 1/sqrt(2*sqrt(POINTS*DIM));
	tau_ = 1/(sqrt(2*POINTS*DIM));

	file = fopen("iris.txt" , "w");
	fputs("Simulation - Starting...\n", file);
	fclose(file);
	createParents( parents );


	for(i=0;i<ITERATIONS;i++){
		mutation( parents, offsprings );
		survivorSelection( parents, offsprings );
		if(i==(long)(count_ * ITERATIONS)){
			file = fopen("iris.txt" , "r+");
			sprintf(str, "Simulation - %.0f%% completed...\n", (count_ * 100) );
			fputs(str, file);
			fclose(file);
			count_ += 0.1;
		}
	}

	file = fopen("iris.txt" , "r+");

	position = bestIndividuo( parents, NUM_PARENTS );

	sprintf(str, "Simulation - %.0f%% completed...\n", (count_ * 100) );
	fputs(str, file);
	fputs("\nBest Individuo - Simulation Iris\n\nA\n", file);
	for(y=0;y<POINTS;y++){
		for(i=0;i<DIM;i++){
			sprintf(str, "%f | ", parents[position].A[y*DIM + i]);
			fputs(str, file);
		}
		fputs("\n", file);
	}
	fputs("\nB\n", file);
	for(y=0;y<POINTS;y++){
		for(i=0;i<DIM;i++){
			sprintf(str, "%f | ", parents[position].B[y*DIM + i]);
			fputs(str, file);
		}
		fputs("\n", file);
	}
	sprintf(str, "\n%f", parents[position].fitness);
	fputs(str, file);
	fclose(file);

	return EXIT_SUCCESS;
}

void createIndividuo( struct individuo *individuo)
{
	long i=0, j, count=0;
	int list[2] = {-1,1}, mult_a, mult_b;

	for(j=0;j<CLASSES;j++){
		mult_a = random_list(list, 2);
		mult_b = random_list(list, 2);
		count += points_classes[j];
		for(i=(count - points_classes[j])*DIM;i<count*DIM;i++){
			individuo->A[i] = mult_a*random_uniform( 0, matrix_z[i] );
			individuo->B[i] = mult_b*sqrt( pow(matrix_z[i], 2.0) - pow(individuo->A[i], 2.0) );
			individuo->signal[i] = mult_b;
			individuo->sigma[i] = 1.0;
		}
	}
	fitness( individuo );
}

void fitness( struct individuo *individuo)
{
	float list_Zc_a[CLASSES*DIM];
	float list_Zc_b[CLASSES*DIM];
	float sum_a = 0.0, sum_b = 0.0;
	float sum_distance_Zc = 0.0;
	float sum_distance_Zwc = 0.0;
	float sum_v = 0.0;
	long count = 0, j = 0, x, i;

	for(x=0;x<CLASSES;x++){
		count += points_classes[x];
		for(i=0;i<DIM;i++){
			sum_a = 0.0;
			sum_b = 0.0;
			for(j=count - points_classes[x];j<count;j++){
				sum_a = float_sum(sum_a, individuo->A[j*DIM + i]);
				sum_b = float_sum(sum_b, individuo->B[j*DIM + i]);
			}
			list_Zc_a[x*DIM + i] = sum_a/points_classes[x];
			list_Zc_b[x*DIM + i] = sum_b/points_classes[x];
		}
	}

	for(x=0;x<CLASSES-1;x++){
		for(i=x+1;i<CLASSES;i++){
			for(j=0;j<DIM;j++){
				sum_distance_Zc = float_sum(sum_distance_Zc,
				distance(list_Zc_a[x*DIM + j],list_Zc_b[x*DIM + j],
						list_Zc_a[i*DIM + j], list_Zc_b[i*DIM + j]) );
			}
		}
	}

	for(x=0;x<CLASSES;x++){
		sum_distance_Zwc = 0.0;
		for(i=0;i<points_classes[x];i++){
			for(j=0;j<DIM;j++){
				sum_distance_Zwc = float_sum(sum_distance_Zwc,
				distance(list_Zc_a[x*DIM + j],list_Zc_b[x*DIM + j],
						individuo->A[i*DIM + j], individuo->B[i*DIM + j]) );
			}
		}
		sum_v = float_sum(sum_v, sqrt(sum_distance_Zwc)/(points_classes[x]-1));
	}
	individuo->fitness = sum_distance_Zc/(1 + sum_v);
}

void createParents(struct individuo *parents_)
{
	int i;

	for(i=0;i<NUM_PARENTS;i++){
		createIndividuo( &parents_[i] );
	}
}

void mutation(struct individuo *parents_, struct individuo *offsprings_)
{
	int i, j, x, position;
	float normal;

	for(x=0;x<NUM_PARENTS;x++){
		for(i=0;i<NUM_OFFSPRINGS_PER_PARENTS;i++){
			position = i + (i*x);
			normal = normal_variate(0.0, 1.0);
			for(j=0;j<POINTS*DIM;j++){
				//calculo de Sigma
				offsprings_[position].sigma[j] = parents_[x].sigma[j] * exp((tau_*normal) + (tau*normal_variate(0.0, 1.0)));
				//calculo de A
				offsprings_[position].A[j] = parents_[x].A[j] + offsprings_[i + (i*x)].sigma[j]*normal_variate(0.0, 1.0);
				if(matrix_z[j] >= 0){
					if(offsprings_[position].A[j] > matrix_z[j]) offsprings_[position].A[j] = fmod(offsprings_[position].A[j], matrix_z[j]);
					else if(offsprings_[position].A[j] < -matrix_z[j]) offsprings_[position].A[j] = fmod(offsprings_[position].A[j], -matrix_z[j]);
				}
				else{
					if(offsprings_[position].A[j] < matrix_z[j]) offsprings_[position].A[j] = fmod(offsprings_[position].A[j], matrix_z[j]);
					else if(offsprings_[position].A[j] > -matrix_z[j]) offsprings_[position].A[j] = fmod(offsprings_[position].A[j], -matrix_z[j]);
				}
				//calculo de B
				offsprings_[position].signal[j] = parents_[x].signal[j] + normal_variate(0.0, 1.0);
				if(offsprings_[position].signal[j] > 1.0) offsprings_[position].signal[j] = fmod(offsprings_[position].signal[j], 1.0);
				else if(offsprings_[position].signal[j] < -1.0) offsprings_[position].signal[j] = fmod(offsprings_[position].signal[j], -1.0);

				if(offsprings_[position].signal[j] >= 0) offsprings_[position].B[j] = sqrt( pow(matrix_z[j], 2.0) - pow(offsprings_[position].A[j], 2.0) );
				else offsprings_[position].B[j] = -sqrt( pow(matrix_z[j], 2.0) - pow(offsprings_[position].A[j], 2.0) );
				
			}
			fitness(&offsprings_[position]);
		}
	}

}

long worstIndividuo( struct individuo *list_individuo, long dimension )
{
	long i, position = 0;

	for(i=1;i<dimension;i++){
		if(list_individuo[i].fitness < list_individuo[position].fitness) position = i;
	}
	return position;
}

long bestIndividuo( struct individuo *list_individuo, long dimension )
{
	long i, position = 0;

	for(i=1;i<dimension;i++){
		if(list_individuo[i].fitness > list_individuo[position].fitness) position = i;
	}
	return position;
}

void survivorSelection(struct individuo *parents_, struct individuo *offsprings_)
{
	int i, worst;

	for(i=0;i<NUM_OFFSPRINGS_PER_PARENTS*NUM_PARENTS; i++){
		worst = worstIndividuo(parents_, NUM_PARENTS);
		if(parents_[worst].fitness < offsprings_[i].fitness) parents_[worst] = offsprings_[i];
	}

}
